﻿namespace Moly.misc
{
    using System;
    using System.Collections.Generic;
    using System.Runtime.InteropServices;
    using System.Runtime.InteropServices.ComTypes;
    using System.Text;
    using Microsoft.Win32;

    public class API
    {
        #region LoadLibraryFlags enum

        [Flags]
        public enum LoadLibraryFlags : uint
        {
            DONT_RESOLVE_DLL_REFERENCES = 0x00000001,
            LOAD_IGNORE_CODE_AUTHZ_LEVEL = 0x00000010,
            LOAD_LIBRARY_AS_DATAFILE = 0x00000002,
            LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008,
        }

        #endregion

        #region OLEMISC enum

        public enum OLEMISC
        {
            OLEMISC_RECOMPOSEONRESIZE = 0x1,
            OLEMISC_ONLYICONIC = 0x2,
            OLEMISC_INSERTNOTREPLACE = 0x4,
            OLEMISC_STATIC = 0x8,
            OLEMISC_CANTLINKINSIDE = 0x10,
            OLEMISC_CANLINKBYOLE1 = 0x20,
            OLEMISC_ISLINKOBJECT = 0x40,
            OLEMISC_INSIDEOUT = 0x80,
            OLEMISC_ACTIVATEWHENVISIBLE = 0x100,
            OLEMISC_RENDERINGISDEVICEINDEPENDENT = 0x200,
            OLEMISC_INVISIBLEATRUNTIME = 0x400,
            OLEMISC_ALWAYSRUN = 0x800,
            OLEMISC_ACTSLIKEBUTTON = 0x1000,
            OLEMISC_ACTSLIKELABEL = 0x2000,
            OLEMISC_NOUIACTIVATE = 0x4000,
            OLEMISC_ALIGNABLE = 0x8000,
            OLEMISC_SIMPLEFRAME = 0x10000,
            OLEMISC_SETCLIENTSITEFIRST = 0x20000,
            OLEMISC_IMEMODE = 0x40000,
            OLEMISC_IGNOREACTIVATEWHENVISIBLE = 0x80000,
            OLEMISC_WANTSTOMENUMERGE = 0x100000,
            OLEMISC_SUPPORTSMULTILEVELUNDO = 0x200000
        }

        #endregion

        #region RegKind enum

        public enum RegKind
        {
            RegKind_Default = 0,
            RegKind_Register = 1,
            RegKind_None = 2
        }

        #endregion
        
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern uint GetShortPathName([MarshalAs(UnmanagedType.LPTStr)] string lpszLongPath,
            [MarshalAs(UnmanagedType.LPTStr)]StringBuilder lpszShortPath,
            uint cchBuffer);

        [DllImport("kernel32.dll", EntryPoint = "FreeLibrary", SetLastError = true)]
        public static extern bool FreeLibrary(IntPtr hModule);

        [DllImport("oleaut32.dll", PreserveSig = false)]
        public static extern void RegisterTypeLib(ITypeLib ptlib,
                                                  [MarshalAs(UnmanagedType.BStr)] string szFullPath,
                                                  [MarshalAs(UnmanagedType.BStr)] string szHelpDir);

        [DllImport("kernel32.dll", EntryPoint = "LoadLibraryExW", CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern IntPtr LoadLibraryEx([MarshalAs(UnmanagedType.LPWStr)] string lpFileName, IntPtr hFile,
                                                  [MarshalAs(UnmanagedType.U4)] LoadLibraryFlags dwFlags);

        [DllImport("kernel32.dll", CharSet = CharSet.Ansi, EntryPoint = "GetProcAddress", ExactSpelling = true,
            SetLastError = true)]
        public static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);


        public static UInt32 DllRegisterServer(IntPtr hModule)
        {
            IntPtr fptr = GetProcAddress(hModule, "DllRegisterServer");
            if (fptr == IntPtr.Zero)
            {
                throw new Exception("DllRegisterServer not found.");
            }
            var drs = (DRS) Marshal.GetDelegateForFunctionPointer(fptr, typeof (DRS));
            return drs();
        }


        [DllImport("oleaut32.dll", CharSet = CharSet.Unicode, PreserveSig = false)]
        public static extern void LoadTypeLibEx(String strTypeLibName, RegKind regKind,
                                                [MarshalAs(UnmanagedType.Interface)] out ITypeLib typeLib);

        [DllImport("ole32.dll")]
        public static extern int ProgIDFromCLSID([In] ref Guid clsid,
                                                 [MarshalAs(UnmanagedType.LPWStr)] out string lplpszProgID);

        public static string TranslateOleMisc(int misc)
        {
            var x = new List<string>();
            var map = new[]
                          {
                              "recomposeonresize", "onlyiconic", "insertnotreplace", "static", "cantlinkinside",
                              "canlinkbyole1", "islinkobject"
                              , "insideout", "activatewhenvisible", "renderingisdeviceindependent", "invisibleatruntime"
                              , "alwaysrun", "actslikebutton", "actslikelabel",
                              "nouiactivate", "alignable", "simpleframe", "setclientsitefirst", "imemode",
                              "ignoreativatewhenvisible", "wantstomenumerge"
                              , "supportsmultilevelundo"
                          };

            int idx = 0;
            for (int i = 1; i <= 0x200000; i *= 2)
            {
                if ((misc & i) != 0)
                    x.Add(map[idx]);
                idx++;
            }

            return string.Join(",", x);
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern IntPtr BeginUpdateResource(string pFileName,
                                                        [MarshalAs(UnmanagedType.Bool)] bool bDeleteExistingResources);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool UpdateResource(IntPtr hUpdate, short lpType, string lpName, ushort wLanguage,
                                                  byte[] lpData, uint cbData);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool EndUpdateResource(IntPtr hUpdate, bool fDiscard);

        [DllImport("kernel32.dll")]
        public static extern IntPtr FindResource(IntPtr hModule, int lpName, int lpType);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern uint SizeofResource(IntPtr hModule, IntPtr hResInfo);

        [DllImport("kernel32.dll")]
        public static extern IntPtr LockResource(IntPtr hResData);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern IntPtr LoadResource(IntPtr hModule, IntPtr hResInfo);

        [DllImport("version.dll")]
        public static extern bool VerQueryValue(byte[] buffer, string subblock, out IntPtr blockbuffer, out uint len);

        #region Nested type: DRS

        internal delegate UInt32 DRS();

        #endregion

        #region Nested type: VS_FIXEDFILEINFO

        [StructLayout(LayoutKind.Sequential)]
        public struct VS_FIXEDFILEINFO
        {
            public UInt32 dwSignature;
            public UInt32 dwStrucVersion;
            public UInt32 dwFileVersionMS;
            public UInt32 dwFileVersionLS;
            public UInt32 dwProductVersionMS;
            public UInt32 dwProductVersionLS;
            public UInt32 dwFileFlagsMask;
            public UInt32 dwFileFlags;
            public UInt32 dwFileOS;
            public UInt32 dwFileType;
            public UInt32 dwFileSubtype;
            public UInt32 dwFileDateMS;
            public UInt32 dwFileDateLS;
        } ;

        #endregion

        public enum ComLost
        {
            Found,
            Clsid,
            Lid,
            Version,
        }

        private static string toHexVersion(int major, int minor)
        {
            return String.Format("{0}.{1}", major.ToString("x"), minor.ToString("x"));
        }

        private static string toHexVersion(string str)
        {
            var spl = str.Split('.');
            if (spl.Length != 2)
                throw new Exception(string.Format("string '{0}' is not a valid version", str));

            return toHexVersion(Int32.Parse(spl[0]), Int32.Parse(spl[1]));
        }

        public static ComLost ReadTypeLibPathFromReg(string path, ref string dll)
        {
            var spl = path.Split('\\');

            var hkcu = Registry.CurrentUser.OpenSubKey("Software\\Classes");
            var check = hkcu.OpenSubKey(spl[0]);
            if (check != null)
            {
                check.Close();
                hkcu.DeleteSubKeyTree(spl[0]);
            }
            hkcu.Close();

            var root = Registry.ClassesRoot.OpenSubKey("TypeLib").OpenSubKey(spl[0]);
            if (root == null)
            {
                return ComLost.Clsid;
            }
            root = root.OpenSubKey(toHexVersion(spl[1]));
            if (root == null)
            {
                return ComLost.Version;
            }
            root = root.OpenSubKey(spl[2]);
            if (root == null)
            {
                return ComLost.Lid;
            }
            try
            {
                dll = root.OpenSubKey("Win32").GetValue(String.Empty).ToString();
            }
            catch
            {
                return ComLost.Clsid;
            }
            return ComLost.Found;
        }
    }
}